The ZYNQ-7000 is an SoC based on the ARM Cortex-A9 + FPGA architecture. Its boot process is hardware-driven (by the PS) and completes the system startup and programmable logic configuration in stages. The boot process for the ZYNQ 7000 SoC is a multi-stage, highly configurable procedure involving complex interactions between automatic hardware loading and software control. This article will provide a detailed analysis of the entire process, from power-on to the loading of the operating system.
The PS (ARM) and PL (FPGA) are reset.
The state of the BOOT_MODE pins is read to determine the boot medium.
The PL is kept in an unconfigured state.
POR (Power-On Reset) Sequence
All power rails reach a stable state (PS_POR_B signal goes high)
The internal oscillator starts (typical frequency 33.33MHz)
Clock dividers are set to their default values
Clock Network Configuration
x// BootROM initial clock configuration
// Typical initialization valueswritel(ARM_PLL_CTRL, 0x0001A008); // Set PLL to bypass modeThe boot mode is determined by the MIO[5:2] pins:
| MIO[5:2] | Boot Mode | Clock Frequency | Data Width |
|---|---|---|---|
| 0000 | JTAG | - | - |
| 0010 | NAND | 33MHz | 8-bit |
| 0011 | NOR | 50MHz | 16-bit |
| 0101 | QSPI | 100MHz | 4-bit |
| 0110 | SD | 50MHz | 4-bit |
Note: The mode pins are sampled and latched on the rising edge of POR_B
The BootROM code is the first software to execute on a Zynq device after power-on or reset. It starts execution from address 0xFFFF_0000. The BootROM is not accessible to the user. The BootROM code is stored in the on-chip ROM, hence the name BootROM. Because the ZYNQ contains 256K of RAM and 128K of ROM, the BootROM code can be permanently stored in the ROM and will not be lost on power-off. Typically, the internal ROM of a chip is NOR Flash. A key feature of NOR Flash is eXecute In Place (XIP), which allows applications to run directly from the Flash memory without needing to be loaded into the system RAM. It is ROM embedded in the chip and cannot be modified.
Initializes the internal hardware of the PS.
Reads the header of BOOT.BIN and the FSBL from the boot medium.
Reads the boot mode pins to determine the boot device.
Initializes clocks and peripherals.
Reads the boot image from the selected boot device.
Parses the image header and checks its validity.
Loads the FSBL partition into the OCM.
Transfers control to the FSBL.
Initializes the MIO pins, primarily configuring the physical characteristic configuration registers for the MIO pins. The key step is multiplexing MIO40~MIO45 to function as the CLK/CMD/DATA pins for the SD0 peripheral.
Initializes the SD card peripheral and its driver, enabling SD card read/write operations.
Tests SD card read/write functionality.
Reads the BOOT.BIN file from the SD card's file system and parses the BootROM header. The BOOT.BIN file is preceded by a header section, which is organized in a specific format. This header contains information such as the FSBL's load address, size, and its offset within the BOOT.BIN file. The BootROM code is capable of parsing this header.
After obtaining the FSBL code's size, offset, and load address, the BootROM code copies the FSBL code from the BOOT.BIN file into RAM and then jumps to the FSBL's execution address to start it.
At this point, the BootROM has successfully launched the FSBL code.
The execution flow of the BootROM code in QSPI boot mode is as follows:
Initializes the MIO pins, multiplexing the relevant MIO pins to serve the functions required by the QSPI peripheral.
Initializes the QSPI peripheral and its driver for the QSPI Flash device, enabling QSPI read/write operations.
Tests QSPI read/write functionality.
Reads the BOOT.BIN file from the QSPI storage medium and parses the Boot ROM header.
After obtaining the FSBL code's size, offset, and load address, the BootROM code copies the FSBL code from the BOOT.BIN file into RAM and then jumps to the FSBL's execution address to start it.
Unlike the file system search method used for SD cards, in QSPI boot mode, the BootROM code first looks for the BOOT.BIN file at address 0x000000 of the QSPI. If it's not found, it proceeds to the next address, 0x008000. If it's still not found, it jumps to the next address, 0x10000. However, the search range cannot exceed the first 16MB of the QSPI address space.
BOOT.BIN is the core boot image file for the ZYNQ 7000 series SoC. (A boot image is a packaged file containing multiple programs or data required for system startup, typically a binary file used to guide the system through the entire process from power-on to operating system launch. In Zynq, the boot image is usually the BOOT.BIN file. BOOT.BIN is the core file for the entire system startup and must be placed at the beginning of the Zynq boot device. It is read and executed by Zynq's ROM boot code from QSPI, SD card, or NAND.) It is generated by the Bootgen tool based on a .bif file and contains all the components needed for the complete boot chain. For example:
xxxxxxxxxxbifthe_ROM_image:{ [bootloader] fsbl.elf // Partition 1: FSBL, must be first system.bit // Partition 2: FPGA configuration file (optional) u-boot.elf // Partition 3: Second-stage bootloader}In the example above:
| Partition Order | Content | Purpose |
|---|---|---|
| 1 | fsbl.elf | First-stage bootloader, initializes hardware and DDR |
| 2 | system.bit | Configures the PL (FPGA part) |
| 3 | u-boot.elf | Second-stage bootloader, starts Linux or other applications |
xxxxxxxxxx┌───────────────────────┐│ Boot Header │ → Fixed-format header (256 bytes)├───────────────────────┤│ Partition Header Table│ → Partition descriptions (20 bytes/entry)├───────────────────────┤│ FSBL (Required) │ → First-stage bootloader├───────────────────────┤│ Bitstream (Optional)│ → PL configuration data├───────────────────────┤│ SSBL/App (Optional) │ → U-Boot/Linux kernel, applications, etc.├───────────────────────┤│ Auth Cert (Optional) │ → RSA signature/AES encrypted data└───────────────────────┘
The BOOT.BIN header is a data section at the beginning of the BOOT.BIN file, organized in a specific format that can be parsed by the BootROM code. The BootROM reads the Boot Header from the boot image to obtain information about the image, such as:
The starting address of the FSBL.
The source of the encryption key.
The size of the FSBL.
The offset of the FSBL.
A checksum to verify the integrity of the header.
The Boot Header must exist and be correctly formatted. Otherwise, the BootROM will not load the FSBL.
In the BOOT.bin file, the address range from 0 to 0x8FF can be divided into 17 parts, each with a specific meaning.
0x000: Interrupt vector table.
0x020: Fixed value 0xaa995566 (little-endian).
0x024: Fixed value 0x584c4e58 ASCII: XLNX.
0x028: If the value is 0xa5c3c5a3 or 0x3a5c3c5a, the image is encrypted.
0x02C: BootROM header version number, can be ignored.
0x030: This parameter contains the number of bytes from the start of the valid BootROM header to the location of the FSBL/user code image, which is the address offset of the FSBL/user code. This offset must be greater than or equal to 0x8C0.
0x034: Records the length of the FSBL, used to guide the BootROM code in copying the FSBL.
0x038: The load address, specifying where to copy the FSBL into OCM (usually 0x0). It guides the BootROM code on where to copy the FSBL in RAM.
0x03C: The execution address of the FSBL in OCM (usually defined as 0x0). It guides the BootROM code on which RAM address to jump to for execution.
0x040: Records the length of the FSBL.
0x044: Fixed value 0x01.
0x048: Checksum (Calculated by summing the data between 0x020 and 0x047 as 32-bit words and then taking the bitwise NOT. If the sum exceeds 32 bits, only the lower 32 bits are used for the NOT operation).
0x04C-0x097: User-defined area for FSBL/user code. Can be filled with zeros if not needed.
0x098: Offset to the image header table.
0x09C: Location of the partition header table.
0x0A0-0x89F: Parameters for register initialization.
17.0x8C0: The FSBL and user code must be located at or after this address.
The length of the FSBL can be viewed at address 0x034:
The entry address of the interrupt vector table can be viewed at addresses 0x000-0x01F:
The fixed value can be viewed at address 0x020:
Initial PC Pointer
The ARM Cortex-A9 starts execution from address 0x00000000
The first 64KB is the BootROM image (read-only
Initialize DDR controller
Configure MIO pin multiplexing
Set system clocks
Enable interrupt controller
FSBL is the First Stage Bootloader in the Zynq boot process, directly loaded and executed by the BootROM. Its main functions include:
Initialize Hardware: Configure basic peripherals such as DDR memory, clocks, and MIO (Multi-use I/O).
Parse BOOT.BIN: Read the Partition Header Table from the image to identify various partitions (e.g., Bitstream, SSBL, application).
Load Subsequent Images: Copy the second-stage program (e.g., U-Boot, bare-metal application, Linux kernel) to a specified memory address.
Optionally Load PL Configuration: If an FPGA Bitstream exists, FSBL will configure it into the PL (Programmable Logic) section.
Scenario 1: Bare-metal Application (Standalone)
BOOT.BIN Structure:
xxxxxxxxxxBOOT.BIN = FSBL + Bitstream (optional) + Application (.elf)
Process:
BootROM loads FSBL into OCM (On-Chip Memory) and executes it.
FSBL initializes DDR and loads the application (e.g., application.elf) from Flash/QSPI/SD card to a specified address in DDR (defined by the linker script, e.g., 0x00100000).
FSBL jumps to the application's entry point (usually _start or main).
Scenario 2: Running Linux (via U-Boot)
BOOT.BIN Structure:
xxxxxxxxxxBOOT.BIN = FSBL + Bitstream (optional) + U-Boot
Process:
FSBL loads U-Boot (SSBL) into DDR.
U-Boot proceeds to load the Linux kernel (uImage), device tree (.dtb), and root filesystem (e.g., rootfs.cpio) into memory.
U-Boot starts the kernel. At this point, FSBL has exited and is no longer involved in the subsequent process.
Scenario 3: Loading Linux Directly (without U-Boot)
In rare cases, FSBL can directly load the Linux kernel (which needs to be configured in kernel.img format), but using U-Boot as an intermediary is generally recommended.
Without an OS: FSBL jumps directly to the application and does not return.
With U-Boot: FSBL is only responsible for loading U-Boot, which then handles the subsequent boot process.
Bitstream Loading Process:
Transmitted via the PCAP interface
Accelerated using DMA (typical throughput of 400Mbps)
Status check:
xxxxxxxxxx
void load_bitstream(uint32_t* addr, uint32_t size) { writel(PCAP_PROG_B, 0x0); // Assert PROG_B delay(100); writel(PCAP_PROG_B, 0x1); // De-assert PROG_B while(size > 0) { uint32_t data = *addr++; writel(PCAP_DATA, data); size -= 4; while(readl(PCAP_STATUS) & 0x4); // Wait for DMA to be ready }}| Bitstream Size | Configuration Time (100MHz) |
|---|---|
| 1MB | 80ms |
| 5MB | 400ms |
| 10MB | 800ms |
The BOOT.BIN file contains the FSBL image, the u-boot image, and the bitstream file.
The BootROM code needs to find the FSBL by parsing the BOOT.BIN header information. The BootROM code then starts the FSBL.
After the FSBL code runs, it is responsible for finding the U-Boot image and the bitstream file from the BOOT.BIN file, then loading the bitstream file to the ZYNQ PL side, and finally starting U-Boot.
This involves three data tables:
image header table;
partition header table;
image header.
There is only one image header table. The partition header table and image header appear in pairs. The number of images contained in the BOOT.BIN file determines the number of partition header table and image header pairs.
0x00: Version number of the image header table;
0x04: Number of image headers;
0x08: Position offset of the first Partition Header table. This is calculated in words, so the actual offset needs to be multiplied by 4;
0x0C: Position offset of the first Image Header. Measured in words;
0x10: Offset of the header authentication. Measured in words;
0x1C: Padded with 0xFFFFFFFF until the entire image header table is 64 bytes in size.
0x0: Address offset of the next image header. If this is filled with 0, it indicates this is the last image header;
0x4: Position offset of the associated partition header table;
0x8: This address is always 0;
0xC: The value of the actual partition count;
0x10-N: Records the image name.
varies: Used for padding.
0x0: Data length of the encrypted partition; the unit is words, so it must be multiplied by 4 during calculation;
0x4: Data length of the unencrypted partition. If this partition is u-boot, it indicates the length of u-boot, calculated in the same way;
0x8: Total length of encrypted + padding + extension + authentication data;
0xC: Load address for this partition's data, indicating where in memory the data should be copied;
0x10: Execution address for this partition's data, indicating the memory address to jump to when running this partition's code;
0x14: Position offset of this partition's data within the BOOT.BIN file. Copying starts from this address;
0x18: Attribute bits;
0x1C: Section count;
0x20: Position of the checksum field;
0x24: Position of the image header corresponding to this partition header table. In units of words;
0x28: Fields related to encryption;
0x2C-0x38: Undefined;
0x3C: Checksum.
Function: Describes the global attributes of the entire boot image.
Typical Fields:
xxxxxxxxxxtypedef struct { uint32_t magic; // Magic number (e.g., "XNLX") uint32_t version; // Image version uint32_t partition_num; // Number of partitions uint32_t header_checksum;// Header checksum} ImageHeader;Storage Location: The beginning of the image file.
Function: Indexes the metadata of all partitions (not the partition data itself).
Contents:
The offset, size, and load_addr of each partition
Pointer to the partition's authentication certificate
Example:
xxxxxxxxxxtypedef struct { uint32_t partition_offset; // Offset of the partition data within the image uint32_t partition_size; uint32_t load_address; // Address to load into memory uint32_t attribute_word; // Partition attributes (encryption/authentication, etc.)} ImageHeaderTableEntry;Function: Describes the detailed attributes of a single partition.
Typical Fields:
xxxxxxxxxxtypedef struct { uint32_t exec_address; // Execution entry address uint32_t auth_cert_offset; // Authentication certificate offset uint8_t hash[32]; // Partition hash value uint8_t reserved[16];} PartitionHeaderTable;Key Differences:
Each partition has its own independent partition header table
Contains security information specific to the partition (e.g., hash, certificate)
BootROM reads the Image Header to verify the integrity of the image.
FSBL parses the Image Header Table to locate all partitions.
For each partition:
Verify the partition's legitimacy (RSA/hash) via the Partition Header Table.
Load it into memory at the address specified in the Image Header Table.
FSBL is the first user-programmable code executed after the ZYNQ platform powers on. It is primarily responsible for hardware initialization and loading the next-stage bootloader. Its core architecture is as follows:
main() function)xxxxxxxxxxint main(void) { // 1. PS7 initialization (MIO/PLL/clocks/DDR) Status = ps7_init(); // 2. Unlock SLCR registers SlcrUnlock(); // 3. Cache handling Xil_DCacheFlush(); Xil_DCacheDisable(); // 4. Register exception handlers RegisterHandlers(); // 5. Peripheral initialization (based on boot mode) switch(BootModeRegister) { case QSPI_MODE: InitQspi(); case NAND_MODE: InitNand(); case SD_MODE: InitSD(); // ... } // 6. Load boot image HandoffAddress = LoadBootImage(); // 7. Hand off control FsblHandoff(HandoffAddress);}LoadBootImage())xxxxxxxxxxu32 LoadBootImage(void) { // 1. Get boot status and multi-boot register values RebootStatusRegister = Xil_In32(REBOOT_STATUS_REG); MultiBootReg = Xil_In32(XDCFG_MULTIBOOT_ADDR_OFFSET); // 2. Get image start address ImageStartAddress = ComputeImageStart(); // 3. Get partition header info Status = GetPartitionHeaderInfo(ImageStartAddress); // 4. Partition processing loop while (PartitionNum < PartitionCount) { // - Validate partition header Status = ValidateHeader(HeaderPtr); // - Load partition data Status = PartitionMove(ImageStartAddress, HeaderPtr); // - Security verification (RSA/checksum) if (SignedPartitionFlag) { Status = AuthenticatePartition(...); } // - PL bitstream handling if (PLPartitionFlag) { Status = PcapLoadPartition(...); } PartitionNum++; } return ExecAddress; // Return handoff address}PartHeader)xxxxxxxxxxtypedef struct { u32 image_word_len; // Partition length in the image (in words) u32 data_word_len; // Actual data length (in words) u32 partition_word_len;// Total partition length (in words) u32 load_addr; // Load address (DDR) u32 exec_addr; // Execution address u32 partition_offset; // Partition offset in the image u32 attribute; // Attribute word (encryption/signature, etc.) u32 checksum; // Partition header checksum} PartHeader;xxxxxxxxxx// PL bitstream partition// PS code partition// RSA signature present// Partition ownerxxxxxxxxxx// RSA signature verificationStatus AuthenticatePartition(u8 *PartitionStart, u32 Length) { // 1. Verify signature if (XSecure_RsaVerify(...) != XST_SUCCESS) { return XST_FAILURE; } // 2. Check hash if (ValidateHash(...) != XST_SUCCESS) { return XST_FAILURE; } return XST_SUCCESS;}
// Decrypt encrypted partitionStatus DecryptPartition(u32 Addr, u32 DataLen, u32 ImgLen) { // Decrypt using AES engine XDcfg_AesDecrypt(...);}xxxxxxxxxx0x00000000 - 0x0003FFFF: BootROM + FSBL0x00100000 - 0x001FFFFF: U-Boot0x00200000 - 0x002FFFFF: Device Tree0x03000000 - 0x04000000: Linux Kernel0x08000000 - : Root File Systemxxxxxxxxxxsetenv bootargs 'console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlyprintk'setenv loadaddr 0x03000000setenv fdtaddr 0x00200000bootm ${loadaddr} - ${fdtaddr}Use BBRAM or eFUSE to store the key
CBC mode, 256-bit key
Independent IV for each partition
xxxxxxxxxxvoid set_multiboot(uint32_t offset) { uint32_t val = (offset >> 17) & 0x3FF; // Convert to 128KB blocks writel(MULTIBOOT_REG, val | 0x1); // Enable bit}xxxxxxxxxx0x000000: Golden Image (Backup)0x100000: Primary Image v1.00x200000: Primary Image v2.00x300000: Diagnostic Image| Symptom | Possible Cause | Solution |
|---|---|---|
| Stuck at BootROM | Boot device detection failed | Check MIO pin configuration |
| FSBL fails to load | Insufficient OCM space | Optimize FSBL size to <192KB |
| PL configuration timeout | PCAP interface clock not enabled | Check SLCR register configuration |
Enable QSPI DMA transfer (set QSPI_CR[3]=1)
Run FSBL using XIP (Execute-In-Place) mode
Compress the bitstream (via BITSTREAM.CONFIG.COMPRESS)
xxxxxxxxxx0xF8000000 - System-Level Control Registers0xF8000B00 - MIO Pin Control0xF8000D00 - Clock Controlxxxxxxxxxx0xF8007000 - PCAP Control0xF8007030 - Security Configuration0xF80070A0 - Multiboot ControlUG585: Zynq-7000 Technical Reference Manual
UG821: Zynq-7000 Software Developers Guide
UG873: Embedded Design Tutorial
XAPP1175: Secure Boot of Zynq-7000 SoC